<!-- Copyright 2023 OpenObserve Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

-->

<template>
      <q-page class=" quota-page text-left"
        :class="store.state.theme === 'dark' ? 'dark-theme-page' : 'light-theme-page'"
       style="min-height: inherit">
        <div :style="{ height: '100%', marginTop: 0 }" class="app-table-container" >
            <div class="q-px-md q-pt-sm">
                <div
                    class="q-table__title full-width q-pb-sm "
                    data-test="user-title-text"
                >
                    {{ t("quota.header") }}
                </div>
                <div class="flex items-center justify-between full-width q-mb-sm">
                    <div class="flex items-center ">
                        <q-select
                        :loading="isOrgLoading"
                        v-model="selectedOrganization"
                        :options="organizationToDisplay"
                        @filter="filterOrganizations"
                        placeholder="Select Organization"
                        :popup-content-style="{ textTransform: 'lowercase' }"
                        color="input-border"
                        bg-color="input-bg"
                        class="q-py-sm no-case q-mr-md input-width org-select"
                        stack-label
                        outlined
                        filled
                        dense
                        use-input
                        hide-selected
                        fill-input                    
                        @update:model-value="updateOrganization()"
                        :rules="[(val: any) => !!val || 'Field is required!']"
                    
                    >
                    </q-select>
                    <div
                     class="quota-tabs">
                        <q-tabs
                        data-test="quota-tabs"
                        :model-value="activeTab"
                        no-caps
                        outside-arrows
                        size="sm"
                        mobile-arrows
                        class="bg-white text-primary"
                        @update:model-value="updateActiveTab"
                         >
                        <q-tab
                        data-test="quota-api-limit-tab"
                        name="api-limits"
                        :label="t('quota.api-limits')"
                        />
                        <q-tab
                        data-test="quota-role-limit-tab"
                        name="role-limits"
                        :label="t('quota.role-limits')"
                        />
                    </q-tabs>
                    </div>
                    </div>
                    <div  class="flex items-center" v-if="selectedOrganization">
                        <q-btn
                            v-if="!editTable"
                            data-test="edit-table-btn"
                            label="Edit Quota"
                            class="border  title-height"
                            no-caps
                            :disable="activeTab == 'role-limits' && !expandedRow"
                            @click="editTableWithInput"
                        >
                        <q-icon name="edit" style="font-weight: 200; opacity: 0.7;"  class="q-ml-sm"/>
                    </q-btn>


                    </div>

                </div>
                <div class="flex items-center justify-between full-width q-mb-sm">
                <div v-if="selectedOrganization && activeType == 'table'" class="flex items-center ">
                    <q-input
                        data-test="pipeline-list-search-input"
                        v-model="searchQuery"
                        borderless
                        filled
                        dense
                        class=" no-border input-width"
                        :placeholder="{
                            'api-limits': t('quota.api-search'),
                            'role-limits': t('quota.role-search')
                        }[activeTab]"
                    >
                        <template #prepend>
                        <q-icon name="search" class="cursor-pointer" />
                        </template>
                    </q-input>
                    <q-select
                        v-if="activeTab == 'role-limits'"
                        :loading="isApiCategoryLoading"
                        v-model="selectedApiCategory"
                        :options="filteredApiCategoryToDisplayOptions"
                        placeholder="Select API Category"
                        color="input-border"
                        style="padding: 0px;"
                        bg-color="input-bg"
                        class=" no-case q-mr-md input-width q-ml-md category-select"
                        stack-label
                        outlined
                        filled
                        dense
                        use-input
                        hide-selected
                        fill-input                    
                        clearable
                        @filter="filterApiCategoriesToDisplayOptions"
                        @update:model-value="filterModulesBasedOnCategory()"
                  >
                    </q-select>
                 </div>
                    <div
                        v-if="selectedOrganization"
                        class="quota-tabs float-right q-ml-auto">
                        <q-tabs
                        data-test="table-json-type-selection-tabs"
                        :model-value="activeType"
                        no-caps
                        outside-arrows
                        size="sm"
                        mobile-arrows
                        class="bg-white text-primary"
                        @update:model-value="updateActiveType"
                        >
                        <q-tab
                        data-test="table-json-type-selection-tab"
                        name="table"
                        :label="t('quota.table')"
                        />
                        <q-tab
                        :disable=" activeTab == 'role-limits' && !expandedRow"
                        data-test="table-json-type-selection-tab"
                        name="json"
                        :label="t('quota.json')"
                        />
                    </q-tabs>
                    </div>
                </div>
            </div>

            <q-table
            :rows="apiLimitsRows"
            :columns="generateColumns()"
            row-key="name"
            :pagination="pagination"
            :filter="searchQuery"
            :filter-method="filteredData"
            v-if="activeTab == 'api-limits' && activeType == 'table'"
            dense
            class="q-mx-md"
            >
            <template v-slot:header="props">
                <q-tr  :props="props" class="thead-sticky ">
                <q-th
                    v-for="col in props.cols"
                    :key="col.name"
                    :props="props"
                    :style="col.style"
                >
                    {{ col.label }}
                </q-th>
                </q-tr>
            </template>
            <template #no-data></template>

            <template #bottom="scope">
                <q-table-pagination
                :scope="scope"
                :resultTotal="resultTotal"
                position="bottom"
                :perPageOptions="perPageOptions"
                @update:changeRecordPerPage="changePagination"
            />
            </template>

            <template v-slot:body-cell="props">
            <q-td  :props="props" v-if="editTable" 
            :style="{
                    backgroundColor: editTable && props.col.name !== 'module_name' ? store.state.theme === 'dark' ? '#212121' : '#f1f1ee' : 'transparent',
                    }"
            >
                <div v-if="props.col.name != 'module_name' && props.row[props.col.name] != '-'" contenteditable="true"
                debounce="500"
                :class="{
                    'editable-cell': editTable && props.col.name !== 'module_name',
                    'edited-input': isEdited(props.row.module_name, props.col.name)
                }"
                @input="(event: any) => handleInputChange('' , props.row.module_name , props.row[props.col.name], props.col.name, event.target.innerText)"
                @keypress="restrictToNumbers"
                @paste="preventNonNumericPaste"
                >
                    {{ changedValues[props.row.module_name]?.[props.col.name] ?? props.row[props.col.name]}}
                </div>
                    <div v-else-if="props.col.name == 'module_name'"> 
                    {{ props.row[ props.col.name ] }}
                </div>
                <div
                :disabled="true" v-else-if="props.row[props.col.name] == '-'">
                -
                </div>
            </q-td>
            <q-td  :props="props" v-else>
                <div v-if="props.col.name != 'module_name' && props.row[props.col.name] != '-'">
                    {{ props.row[ props.col.name ] }}
                </div>
                    <div v-else-if="props.col.name == 'module_name'"> 
                    {{ props.row[ props.col.name ] }}
                </div>
                <div v-else-if="props.row[props.col.name] == '-'">
                -
                </div>
            </q-td>
            </template>
            </q-table>
            <div class="q-mt-md" v-if="activeTab == 'api-limits'  && activeType == 'json'" style="height: calc(100vh - 245px); ">
            <query-editor
                    data-test="json-view-roles-editor"
                    ref="queryEditorRef"
                    editor-id="json-view-roles-editor"
                    class="monaco-editor"
                    :debounceTime="300"
                    v-model:query="jsonStrToDisplay"
                    language="json"
                    style="height: 100%;"
                    :read-only="!editTable"
                />
            </div>
            <q-table
                :rows="rolesLimitRows"
                :columns="roleLimitsColumns"
                row-key="name"
                :pagination="pagination"
                :filter="searchQuery"
                :filter-method="filteredData"
                dense
                v-if="activeTab == 'role-limits' && activeType == 'table'"
                class="q-mx-md"
                >
                <template v-slot:header="props">
                    <q-tr :props="props" class="thead-sticky">
                        <q-th
                        v-for="col in props.cols"
                        :key="col.name"
                        :props="props"
                        :style="col.style"
                        >
                        {{ col.label }}
                        </q-th>
                    </q-tr>
                </template>
                <template #no-data></template>
                <template #bottom="scope">
                    <q-table-pagination
                    :scope="scope"
                    :resultTotal="resultTotal"
                    position="bottom"
                    :perPageOptions="perPageOptions"
                    @update:changeRecordPerPage="changePagination"
                />
                </template>
                <template v-slot:body="props">
                    <q-tr
                        :data-test="`quota-role-list-table-${props.row.uuid}-row`"
                        :props="props"
                        style="cursor: pointer"
                    >

                    <q-td v-for="col in roleLimitsColumns" :key="col.name" :props="props" :style="col.style">
                        <template v-if="col.name == 'role_name'">
                            <q-btn
                                dense
                                flat
                                size="xs"
                                :icon=" 
                            expandedRow != props.row.uuid
                                ? 'chevron_right'
                                : 'expand_more'
                            "
                        @click="triggerExpand(props)"
                        />
                            {{ props.row[col.name] }}
                        </template>
                        <template v-else>
                            
                        </template>
                        
                    </q-td>
                    </q-tr>
                    <q-tr v-if="!editTable && !roleLevelLoading" v-for="(row,index) in filteredRoleLevelModuleRows" data-test="scheduled-pipeline-row-expand" v-show="expandedRow === props.row.uuid" :props="props" >
                        <q-td v-for="col in props.cols" :key="col.name" :props="props">
                        <template v-if="col.name == 'role_name'">
                            <div style="padding-left: 20px;">
                                {{ row['module_name'] }}
                            </div>
                        </template>
                        <template v-else-if="col.name == '#'"> 
                            {{}}
                        </template>
                        <template v-else-if="row[col.name] == '-'">
                            -
                        </template>
                        <template v-else> 
                            {{ row[col.name] }}
                        </template>

                        </q-td>
                    </q-tr>
                    <q-tr v-if="editTable && !roleLevelLoading" v-for="(row,index) in filteredRoleLevelModuleRows" data-test="scheduled-pipeline-row-expand" v-show="expandedRow === props.row.uuid" :props="props" >
                            <q-td
                            :style="{
                                    backgroundColor: editTable && col.name !== 'role_name' ? store.state.theme === 'dark' ? '#212121' : '#f1f1ee' : 'transparent',
                                    }"

                        
                        :props="props" 
                            v-for="col in props.cols" :key="col.name" 
                            v-if="editTable"
                            style="padding-left: 8px ;">
                            <template v-if="col.name == 'role_name'">
                                <div style="padding-left: 20px;">
                                    {{ row['module_name'] }}
                                </div>
                            </template>
                            <template v-else-if="col.name == '#'"> 
                                {{}}
                            </template>
                            <template v-else-if="row[col.name] == '-'">
                                -
                            </template>
                            <template v-else> 
                                <div
                                contenteditable="true"
                                debounce="500"
                                :class="{
                                    'editable-cell': editTable && col.name !== 'module_name',
                                    'edited-input': isEdited(row.module_name, col.name)
                                }"
                                @input="(event: any) => handleInputChange(props.row.role_name , row.module_name , row[col.name], col.name, event.target.innerText)"
                                @keypress="restrictToNumbers"
                                @paste="preventNonNumericPaste"
                                >
                                {{ row[col.name] }}
                                </div>
                            
                            </template>
                        </q-td>
                    </q-tr>
                </template>

            </q-table>
            <div class="q-mt-md" v-if="activeTab == 'role-limits' && activeType == 'json'" style="height: calc(100vh - 245px);">
                <query-editor
                    data-test="json-view-roles-editor"
                    ref="queryEditorRef"
                    editor-id="json-view-roles-editor"
                    class="monaco-editor"
                    :debounceTime="300"
                    v-model:query="jsonStrToDisplay"
                    language="json"
                    style="height: 100%;"
                    :read-only="!editTable"
                    />
            </div>
        <div v-if=" (activeTab == 'api-limits' || activeTab == 'role-limits') && loading && !apiLimitsRows.length" class="flex justify-center items-center">
            <q-spinner-hourglass color="primary" size="lg" />
        </div>
        <div v-else-if=" ((activeTab == 'api-limits') && !loading && !selectedOrganization )">
            <NoOrganizationSelected />
        </div>
        <div v-else-if="((activeTab == 'role-limits') && !loading && !selectedOrganization )">
            <NoOrganizationSelected />
        </div>
        <div v-else-if="(activeTab == 'api-limits') && !loading && !apiLimitsRows.length">
            <NoData />
        </div>
        <div v-else-if="(activeTab == 'role-limits') && !loading && !rolesLimitRows.length">
            <NoData />
        </div>
        <div class="flex justify-end w-full tw-ml-auto floating-buttons  q-pr-md"
            v-if="editTable && activeType == 'table'"
            >
            <q-btn
            label="Cancel"
            class="border q-ml-md title-height"
            no-caps
            @click="cancelChanges"
            />
            <q-btn
            label="Save Changes"
            class="text-bold no-border q-ml-md"
                :color="(Object.keys(changedValues).length > 0) ? 'secondary' : 'grey'"
                :disable="(Object.keys(changedValues).length === 0) "
                padding="sm md"
                no-caps
            @click="saveChanges"
            />
        </div>
        <div class="flex justify-end w-full tw-ml-auto floating-buttons  q-pr-md q-mt-md"
            v-if="editTable && activeType == 'json'"
            >
            <q-btn
            label="Cancel"
            class="border q-ml-md title-height"
            no-caps
            @click="cancelJsonChanges"
            :disable="isSavingJson"
            />
            <q-btn
            :label="isSavingJson ? 'Saving Changes...' : 'Save Changes'"
            class="text-bold no-border q-ml-md"
                color="secondary"
                padding="sm md"
                no-caps
            @click="saveJsonChanges"
            :disable="isSavingJson"

            />
        </div>
        
        </div>

        <ConfirmDialog
        title="UnSaved Changes Detected"
        message="save changes before switching tabs"
        @update:ok="saveChangesAndTabSwitch"
        @update:cancel="discardChangesTabSwitch"
        v-model="showConfirmDialogTabSwitch"
        />
        <ConfirmDialog
        title="UnSaved Changes Detected"
        message="save changes before expanding another row"
        @update:ok="saveChangesAndRoleSwitch"
        @update:cancel="discardChangesRoleSwitch"
        v-model="showConfirmDialogRowSwitch"
        />
        <ConfirmDialog
        title="UnSaved Changes Detected"
        message="save changes before switching Type"
        @update:ok="saveChangesAndTypeSwitch"
        @update:cancel="discardChangesTypeSwitch"
        v-model="showConfirmDialogTypeSwitch"
        />
      </q-page>
      

</template>

<script lang="ts">
import { useI18n } from "vue-i18n";
import { computed, defineComponent, onMounted, reactive, ref, watch } from "vue";
import NoOrganizationSelected from "@/components/shared/grid/NoOrganizationSelected.vue";
import { useStore } from "vuex";
import organizationsService from "@/services/organizations";
import AppTabs from "@/components/common/AppTabs.vue";
import QTablePagination from "@/components/shared/grid/Pagination.vue";
import { getRoles } from "@/services/iam";
import ratelimitService from "@/services/rate_limit";
import { useQuasar } from "quasar";
import { useRouter } from "vue-router";
import { getImageURL, getUUID } from "@/utils/zincutils";
import ConfirmDialog from "@/components/ConfirmDialog.vue";
import QueryEditor from "@/components/QueryEditor.vue";

import useRateLimiter from "@/composables/useRateLimiter";
import {
  outlinedDelete,
  outlinedPause,
  outlinedPlayArrow,

  outlinedFileDownload,
  outlinedFileUpload,
  outlinedInsertDriveFile
} from "@quasar/extras/material-icons-outlined";
import AppTable from "@/components/AppTable.vue";
import NoData from "@/components/shared/grid/NoData.vue";
export default defineComponent ({
    name: "Quota",
    components: {
        NoOrganizationSelected ,
        AppTabs,
        QTablePagination,
        ConfirmDialog,
        QueryEditor,
        NoData
    },
    setup() {
        const { t } = useI18n();
        const selectedOrganization = ref<any>(null);
        const store = useStore();
        const $q = useQuasar();
        const organizations = ref<any[]>([]);
        const isOrgLoading = ref<boolean>(false);
        const resultTotal = ref<number>(0);
        const perPageOptions = ref<number[]>([10, 20, 50, 100]);
        const { getApiLimitsByOrganization, getRoleLimitsByOrganization , getModulesToDisplay} = useRateLimiter();
        const pagination: any = ref({
            rowsPerPage: 20,
            });
        const rolesLimitRows = ref<any[]>([]);
        const rolesColumns = ref<any[]>([]);
        const activeType = ref<any>("table");
        const tabs = ref<any[]>([
            {
                label: "API Limits",
                value: "api-limits"
            },
            {
                label: "Role Limits",
                value: "role-limits"
            }
        ]);
        const apiLimitsColumns: any = [
            {
                name: "module_name",
                field: "module_name",
                label: t("quota.moduleName"),
                align: "left",
                sortable: true,
            },
            {
                name: "list",
                field: "list",
                label: t("quota.listLimit"),
                align: "center",
                sortable: true,
                style: 'width: 200px !important; '
            },
            {
                name: "get",
                field: "get",
                label: t("quota.getLimit"),
                align: "center",
                sortable: true,
                style: 'width: 200px !important; '
            },
            {
                name: "create",
                field: "create",
                label: t("quota.createLimit"),
                align: "center",
                sortable: true,
                style: 'width: 200px !important; '
            },
            {
                name: "update",
                field: "update",
                label: t("quota.updateLimit"),
                align: "center",
                sortable: true,
                style: 'width: 200px !important; '
            },
            {
                name: "delete",
                field: "delete",
                label: t("quota.deleteLimit"),
                align: "center",
                sortable: true,
                style: 'width: 200px !important; '
            }
        ];
        const roleLimitsColumns: any = [
        {
            name: "role_name",
            field: "role_name",
            label: t("quota.roleName"),
            align: "left",
            sortable: true,

        },
        {
            name: "list",
            field: "list",
            label: t("quota.listLimit"),
            align: "center",
            sortable: true,
            style: 'width: 200px !important; '
        },
        {
            name: "get",
            field: "get",
            label: t("quota.getLimit"),
            align: "center",
            sortable: true,
            style: 'width: 200px !important; '
        },
        {
            name: "create",
            field: "create",
            label: t("quota.createLimit"),
            align: "center",
            sortable: true,
            style: 'width: 200px !important; '

        },
        {
            name: "update",
            field: "update",
            label: t("quota.updateLimit"),
            align: "center",
            sortable: true,
            style: 'width: 200px !important; '
        },
        {
            name: "delete",
            field: "delete",
            label: t("quota.deleteLimit"),
            align: "center",
            sortable: true,
            style: 'width: 200px !important; '
        }
        ];
        const activeTab = ref<string>("api-limits");
        const searchQuery = ref<string>("");
        const apiLimitsRows = ref<any[]>([]);
        const editTable = ref<boolean>(false);
        const changedValues = ref<any>({});
        const router = useRouter();
        const loading = ref<boolean>(false);
        const isBulkUpdate = ref<boolean>(false);
        const uploadedRules = ref<any>(null);
        const fileListToDisplay = ref<string>("");
        const uploadingRules = ref<boolean>(false);
        const uploadError = ref<string>("");
        const focusedInputId = ref<any>(null);
        const expandedRow = ref<any>("");
        const roleLevelModuleRows = ref<any[]>([]);
        const roleLevelLoading = ref<boolean>(false);

        const openedRole = ref<any>("");
        const selectedApiCategory = ref<any>(null);
        const rolesToBeDisplayed = ref<any[]>([]);
        const isApiCategoryLoading = ref<boolean>(false);
        const filteredRoleLevelModuleRows = ref<any[]>([]);
        const expandedRole = ref<string>("");
        const showConfirmDialogTabSwitch = ref(false);
        const showConfirmDialogRowSwitch = ref(false);
        const nextTab = ref<string | null>(null);
        const toBeExpandedRow = ref<any>(null);
        const jsonStrToDisplay = ref<string>("");
        const nextType = ref<string | null>(null);
        const showConfirmDialogTypeSwitch = ref(false);
        const isSavingJson = ref<boolean>(false);
        const filteredOrganizations = ref<any[]>([]);
        const filteredApiCategoryToDisplayOptions = ref<any[]>([]);
        const apiCategories = ref<any[]>([]);

        const selectedPerPage = ref<number>(20);
        const qTable = ref<any>(null);

    onMounted(async ()=>{
        await getOrganizations();
        if(router.currentRoute.value.query.quota_org){
            selectedOrganization.value = {
                label: router.currentRoute.value.query.quota_org,
                
                value: router.currentRoute.value.query.quota_org
            }
        }
        else if(organizations.value.length > 0){
            selectedOrganization.value = organizations.value[0];
        }
        if(selectedOrganization.value){
            if(activeTab.value === "api-limits"){
                //here we are getting the api limits for the selected organization
                apiLimitsRows.value = await getApiLimitsByOrganization(selectedOrganization.value.value);
                resultTotal.value = apiLimitsRows.value.length;
            }
            else{
                //here we are getting the role limits for the selected organization
                   await getRolesByOrganization();

            }
            //these are the modules that are displayed in the dropdown 
            //to select the api category that user can use to filter the api limits
            if(!store.state.modulesToDisplay[selectedOrganization.value.value]){
                apiCategories.value = await getModulesToDisplay(selectedOrganization.value.value);
            }
            else{
                apiCategories.value = store.state.modulesToDisplay[selectedOrganization.value.value];
            }
        }


    })

    //watch here

    watch (()=>uploadedRules.value, (newVal) => {
        if(!newVal || newVal.length == 0){
            fileListToDisplay.value = '';
            return;
        }
        fileListToDisplay.value = '';
        if(newVal.length > 1){
        newVal.forEach((file: any) => {
            fileListToDisplay.value += file.name + ',';
        });
    }
    else{
        fileListToDisplay.value = newVal[0].name;
    }


    })
    watch (()=>selectedOrganization.value, async (newVal) => {
        if(newVal && !store.state.modulesToDisplay[newVal.value]){
             apiCategories.value = await getModulesToDisplay(newVal.value);
        }
        else{
            apiCategories.value = store.state.modulesToDisplay[newVal.value];
        }
    })

    //computed here

    const organizationToDisplay = computed (() => {
        if(activeTab.value === "api-limits"){
            const newArray = [...filteredOrganizations.value];
            newArray.unshift({
                label: "global rules",
                value: "global_rules"
            });
            return newArray;
        }
        else{
            return filteredOrganizations.value;
        }
    });
    const updateOrganization = async () => {
        router.push({
            ...router.currentRoute.value,
            query: {
                ...router.currentRoute.value.query,
                quota_org: selectedOrganization.value.value.toLowerCase(),
            }
        })

        if(activeTab.value === "api-limits"){
            if(!store.state.allApiLimitsByOrgId[selectedOrganization.value.value]){
                apiLimitsRows.value = await getApiLimitsByOrganization(selectedOrganization.value.value);
                resultTotal.value = apiLimitsRows.value.length;
            }
            else{
                apiLimitsRows.value = store.state.allApiLimitsByOrgId[selectedOrganization.value.value];
                resultTotal.value = apiLimitsRows.value.length;
            }
        }
        else if(activeTab.value === "role-limits"){
                await getRolesByOrganization();
        }
    }
    const getOrganizations = async () => {
        //this is used to get the organization from the api
        if(store.state.organizations.length === 0){
            try{
                isOrgLoading.value = true;
                const response = await organizationsService.os_list(0, 100000, "id", false, "", "default")
                organizations.value = response.data.data.map((org: any) => ({
                    label: org.name,
                    value: org.identifier
                }));
                organizations.value.sort((a: any, b: any) => a.label.localeCompare(b.label));
                isOrgLoading.value = false;
            }
            catch(error){
                isOrgLoading.value = false;
                console.log(error);
            }
            finally{
                isOrgLoading.value = false;

            }
        }
        else{
            organizations.value = store.state.organizations.map((org: any) => ({
                label: org.name,
                value: org.identifier
            }));

        }
    }
    const updateActiveTab = (tab: string) => {
        let isChanged = Object.keys(changedValues.value).length > 0;

        if (isChanged) {
            nextTab.value = tab; // Store the tab user wants to switch to
            showConfirmDialogTabSwitch.value = true; // Show confirmation dialog
        } else {
            switchTab(tab);
        }
    };
    //this is used to switch the tab
    const switchTab = async (tab: string) => {
        //here when we switch the tab we need to reset the activeType and activeTab
        //active type to table --> reason: sometimes user might have shift from api-limits to role-limits and if activeType is json then none of the row is expanded right
        //which will lead to the issue that user will not be able to see the table data
        activeType.value = 'table'
        activeTab.value = tab;
        //
        if (tab === "role-limits") {
            //here we are checking if the organization is global_rules_meta then we need to reset the selectedOrganization to the first valid organization
            //as we dont support global_rules_meta in role limits
            if(router.currentRoute.value.query.quota_org == "global_rules"){
                selectedOrganization.value = organizations.value[0];
            }
            //here we are getting the roles from the api
            //as we are not storing the roles in the store
            //so we need to get the roles from the api
            await getRolesByOrganization();

        }
        if (tab === "api-limits" ) {
            if(!store.state.allApiLimitsByOrgId[selectedOrganization.value.value]){
                apiLimitsRows.value = await getApiLimitsByOrganization(selectedOrganization.value.value);
            }
            else{
                apiLimitsRows.value = store.state.allApiLimitsByOrgId[selectedOrganization.value.value];
            }
            resultTotal.value = apiLimitsRows.value.length
        }
    };
    //this is used to save the changes and switch the tab 
    //when user confirms the changes and click save in the confirm dialog
    const saveChangesAndTabSwitch = async () => {
        await saveChanges();
        changedValues.value = {};
        editTable.value = false;
        showConfirmDialogTabSwitch.value = false;
        jsonStrToDisplay.value = '';
        if (nextTab.value) {
            switchTab(nextTab.value);
            nextTab.value = null;
        }
    };

    const discardChangesTabSwitch = () => {
        //when user discard the changes and swith the tab we just reset the changedValues and editTable
        //and also we need to reset the activeType
        //we just assign the stored tab value to the activeTab
        changedValues.value = {};
        editTable.value = false;
        if (nextTab.value) {
            switchTab(nextTab.value);
            nextTab.value = null;
        }
        showConfirmDialogTabSwitch.value = false;
    };
    const editTableWithInput = () => {
        editTable.value = true;
    }
    const getRolesByOrganization = async () => {
        //here we are getting the roles from the api 
        //as we are not storing the roles in the store
        //so we need to get the roles from the api
        try{
            const response = await getRoles(selectedOrganization.value?.value);
            rolesLimitRows.value = response.data.map((role: any) => ({
                role_name: role,
                uuid: getUUID(),
                list: 10,
                get: 10,
                create: 10,
                update: 10,
                delete: 10
            }));
            resultTotal.value = rolesLimitRows.value.length;
        }
        catch(error){
            console.log(error);
        }
    }
    const restrictToNumbers = (event: any) => {
    const char = String.fromCharCode(event.keyCode);

    // Allow numbers, backspace, delete, tab, enter, and decimal point
    if (!/[0-9.]/.test(char) && ![8, 46, 9, 13].includes(event.keyCode)) {
        event.preventDefault();
    }
    };
    //this is used for handling the input changes for both api limits and row limits
    const handleInputChange =  (roleName: any = "", moduleName: string, row: any, operation: string, value: any) => {
        let cleanedValue = value.replace(/[^0-9.]/g, "");

        // Prevent multiple dots
        const parts = cleanedValue.split(".");
        if (parts.length > 2) {
        cleanedValue = parts[0] + "." + parts.slice(1).join("");
        }

        // Ensure empty string is stored instead of NaN
        if (cleanedValue === ".") {
        cleanedValue = "";
        }

        // Update value in changedValues
        if (!changedValues.value[moduleName]) {
        changedValues.value[moduleName] = reactive ({});
        }
        changedValues.value[moduleName][operation] = cleanedValue;

        let valueToUpdate ;

        if(isNaN(parseInt(value))){
            valueToUpdate = ""
        }
        else{
        valueToUpdate = parseInt(value);
        }
        changedValues.value[moduleName][operation] = valueToUpdate;
    };

    const saveChanges = async () => {
        try {
            if(!validateChanges(changedValues.value)){
                return;
            }
            let payload: any = {};
            payload = {...changedValues.value};
            let user_role = expandedRole.value;
            let response  = null;
            if(activeTab.value === "api-limits"){
                response = await ratelimitService.update_batch(selectedOrganization.value.value, payload,"module");
            }
            else{
                response = await ratelimitService.update_batch(selectedOrganization.value.value, payload,"role",user_role);
            }
            if(selectedOrganization.value.value == "global_rules"){
                resetStore();
            }
            // Here you would call your API to save the changes
            if(response.status === 200){
                uploadError.value = '';
                uploadedRules.value = [];
                isBulkUpdate.value = false;
                $q.notify({
                    type: "positive",
                    message: response.data.message,
                    timeout: 3000,
                })
            }
        
        // After successful save, refresh the data
        editTable.value = false;
        if(activeTab.value === "api-limits"){
                apiLimitsRows.value = await getApiLimitsByOrganization(selectedOrganization.value.value);
                resultTotal.value = apiLimitsRows.value.length;
            }
            else if(activeTab.value === "role-limits"){                    
                roleLevelModuleRows.value  =  await getRoleLimitsByOrganization(selectedOrganization.value.value, openedRole.value);
                filterModulesBasedOnCategory();
            }
        changedValues.value = {};
    } catch (error: any) {
        $q.notify({
            type: "negative",
            message: error.response.data.message || "Error while updating rate limits rule",
            timeout: 3000,
        })
        console.error('Error saving changes:', error);
    }
    };

    const cancelChanges = () => {
        changedValues.value = {};
        editTable.value = false;
    };

    const cancelJsonChanges = () => {
        changedValues.value = {};
        editTable.value = false;
    };

    const saveJsonChanges = async () => {
        try {
            isSavingJson.value = true;
            let user_role = expandedRole.value;
            const payload = JSON.parse(jsonStrToDisplay.value);
            let response  = null;
            if(activeTab.value === "api-limits"){
                response = await ratelimitService.update_batch(selectedOrganization.value.value, payload,"module");
            }
            else{
                response = await ratelimitService.update_batch(selectedOrganization.value.value, payload,"role",user_role);
            }
            if(selectedOrganization.value.value == "global_rules"){
                resetStore();
            }
            // Here you would call your API to save the changes
            if(response.status === 200){
                $q.notify({
                    type: "positive",
                    message: response.data.message,
                    timeout: 3000,
                })
            }
        
        // After successful save, refresh the data
        editTable.value = false;
        if(activeTab.value === "api-limits"){
                apiLimitsRows.value = await getApiLimitsByOrganization(selectedOrganization.value.value);
                resultTotal.value = apiLimitsRows.value.length;
            }
            else{
                roleLevelModuleRows.value  =  await getRoleLimitsByOrganization(selectedOrganization.value.value, openedRole.value);
                filterModulesBasedOnCategory();
            }
            isSavingJson.value = false;
    } catch (error: any) {
        isSavingJson.value = false;
        $q.notify({
            type: "negative",
            message: error.response.data.message || "Error while updating rate limits rule",
            timeout: 3000,
        })
        console.error('Error saving changes:', error);
    }

    };

    const isEdited = (moduleName: string, operation: string) => {
        return changedValues.value[moduleName] && changedValues.value[moduleName][operation] !== undefined;
    };

    const generateColumns = () => {
        if(selectedOrganization.value?.hasOwnProperty('value') && selectedOrganization.value.value != ''){
            return apiLimitsColumns;
        }
        else{
            return [];
        }
    }

    const downloadTemplate = async () => {
        try{
            const response = await ratelimitService.download_template(selectedOrganization.value.value);
            const blob = new Blob([response.data], { type: 'application/json' });
            const jsonData = JSON.stringify(response.data, null, 2);
            const url = window.URL.createObjectURL(new Blob([jsonData], { type: 'application/json' }));
            const a = document.createElement('a');
            a.href = url;
            a.download = `rate_limit_template_${selectedOrganization.value.label}.json`;
            document.body.appendChild(a);
            a.click();
        }
        catch(error){
            console.log(error);
        }
    }

    const uploadTemplate = async () => {
        const combinedJson = await convertUploadRulesToJson(uploadedRules.value);
        try{
            const dismiss = $q.notify({
                spinner: true,
                message: "Please wait while uploading rules...",
                timeout: 1000,
            });
            uploadingRules.value = true;
            const response = await ratelimitService.upload_template(selectedOrganization.value.value, combinedJson);
            if(response.status === 200){
                $q.notify({
                    type: "positive",
                    message: response.data.message,
                    timeout: 3000,
                })
            }
            uploadingRules.value = false;
            isBulkUpdate.value = false;
            uploadedRules.value = null;
            await getApiLimitsByOrganization(selectedOrganization.value.value);
            dismiss();
        }
        catch(error){
            uploadingRules.value = false;
            uploadError.value = "Error while uploading rules";
        }
        finally{
            uploadingRules.value = false;
        }
    }

    const convertUploadRulesToJson = async (files: any) => {
        let combinedJson: any[] = [];
        
        for (const file of files) {
          try {
            const result: any = await new Promise((resolve) => {
              const reader = new FileReader();
              reader.onload = (e: any) => {
                try {
                  const parsedJson = JSON.parse(e.target.result);
                  // Convert to array if it's a single object
                  const jsonArray = Array.isArray(parsedJson) ? parsedJson : [parsedJson];
                  resolve(jsonArray);
                } catch (error) {
                  $q.notify({
                    message: `Error parsing JSON from file ${file.name}`,
                    color: "negative",
                    position: "bottom",
                    timeout: 2000,
                  });
                  resolve([]);
                }
              };
              reader.readAsText(file);
            });
            
            combinedJson = [...combinedJson, ...result];

          } catch (error) {
            console.error('Error reading file:', error);
          }
        }
        return combinedJson;
    }
    const closeBulkUpdate = () => {
        isBulkUpdate.value = false;
        uploadedRules.value = null;
        uploadError.value = '';
    }
    const onFocus = (row: any) => {
        focusedInputId.value = generateUniqueId(row);
    };

    const generateUniqueId = (row: any) => {
        return row.api_group_name + '_' + row.api_group_operation;
    }
    const filteredData = (rows: any, terms: any) => {
        var filtered = [];
        terms = terms.toLowerCase();
        if(activeTab.value === "api-limits"){
            for (var i = 0; i < rows.length; i++) {
                if(rows[i].module_name.toLowerCase().includes(terms)){
                    filtered.push(rows[i]);
                }
            }
        }
        else{
            for (var i = 0; i < rows.length; i++) {
                if(rows[i].role_name.toLowerCase().includes(terms)){
                    filtered.push(rows[i]);
                }
            }
        }

        return filtered;
    }
    const triggerExpand = async (props : any) =>{
        if(Object.keys(changedValues.value).length > 0){
            showConfirmDialogRowSwitch.value = true;
            toBeExpandedRow.value = props.row;
            return;
        }
        expandedRole.value = props.row.role_name;
        if (expandedRow.value === props.row.uuid) {
            expandedRow.value = null;
            openedRole.value = null;
            } 
        else {
            openedRole.value = props.row.role_name;
            let roleLimits : any;
            if(!store.state.allRoleLimitsByOrgIdByRole[selectedOrganization.value.value]?.[props.row.role_name]){
                roleLevelModuleRows.value =  await getRoleLimitsByOrganization(selectedOrganization.value.value, props.row.role_name);
            }
            else{
                roleLevelModuleRows.value = store.state.allRoleLimitsByOrgIdByRole[selectedOrganization.value.value][props.row.role_name];
            }
            filterModulesBasedOnCategory();
        // Otherwise, expand the clicked row and collapse any other row
            expandedRow.value = props.row.uuid;
        }
    }  

    const preventNonNumericPaste = (event: any) => {
        const clipboardData = event.clipboardData.getData("text");

        // Allow only positive integers (no negatives, no decimals)
        if (!/^\d+$/.test(clipboardData)) {
            event.preventDefault();
        }
    };

    const validateChanges = (changedValues: any) => {
        let isEmpty = false;
        Object.keys(changedValues).forEach((moduleName: any) => {
            Object.keys(changedValues[moduleName]).forEach((operation: any) => {
                if(changedValues[moduleName][operation] === ""){
                    $q.notify({
                        type: "negative",
                        message: "some values are empty please check",
                        timeout: 3000,
                    })
                    isEmpty = true;
                }
            })
        })
        if(isEmpty){
            return false;
        }
        return true;
    }
    const saveChangesAndRoleSwitch = async () => {
        await saveChanges();
        showConfirmDialogRowSwitch.value = false;
        if(toBeExpandedRow.value){
            const index = rolesLimitRows.value.findIndex((row: any) => row.role_name === toBeExpandedRow.value.role_name);
            expandedRow.value = rolesLimitRows.value[index].uuid;
            toBeExpandedRow.value = null;
        }
    }
    const discardChangesRoleSwitch = () => {

        showConfirmDialogRowSwitch.value = false;
        expandedRole.value = toBeExpandedRow.value.role_name;
        expandedRow.value = toBeExpandedRow.value.uuid;
        toBeExpandedRow.value = null;

    }
    const transformData = (data: any) => {
        return data.reduce((acc: any, item: any) => {
            // Convert module_name to snake_case
            const key = item.module_name

        // Remove keys with "-" values
        const filteredItem = Object.fromEntries(
            Object.entries(item).filter(([k, v]) => k !== "module_name" && v !== "-")
        );

        // Assign the cleaned object to the transformed key
        acc[key] = filteredItem;
        return acc;
    }, {});
};
    const updateActiveType = (type: string) => {
        //here we are updating the active type to json mode or table mode
        //if the active type is json then we need to check if the changes are there in the changedValues
        //if the changes are there then we need to show the confirm dialog
        //if the changes are not there then we need to update the active type to json mode

        if(type == 'json'){
            let isChanged = Object.keys(changedValues.value).length > 0;
            if(isChanged && editTable.value){
                nextType.value = type.toLowerCase();
                showConfirmDialogTypeSwitch.value = true;
            }
            else{
                activeType.value = type.toLowerCase();
                editTable.value = false
                populateJsonStr();
            }
        }
        //if the active type is table then we need to check if the changes are there in the jsonStrToDisplay
        //if the changes are there then we need to show the confirm dialog
        //if the changes are not there then we need to update the active type to table mode
        else{
            //here we are checking if any changes are made to the json string so that we can show the confirm dialog
            let isChanged = jsonDiff(jsonStrToDisplay.value, transformData(apiLimitsRows.value));
            if(isChanged && editTable.value){
                //here we store the next type to be used when the user confirms the changes
                nextType.value = type.toLowerCase();
                showConfirmDialogTypeSwitch.value = true;
            }
            //if no changes are made then we are updating the active type to table mode
            else{
                activeType.value = type.toLowerCase();
                editTable.value = false
            }
        }
    }

    const populateJsonStr = () => {
        if(activeTab.value == 'api-limits'){
            jsonStrToDisplay.value = JSON.stringify(transformData(apiLimitsRows.value), null, 2);
        }
        else{
            jsonStrToDisplay.value = JSON.stringify(transformData(filteredRoleLevelModuleRows.value), null, 2);
        }
    }
    const saveChangesAndTypeSwitch = async () => {
        await saveChanges();
        editTable.value = false
        populateJsonStr();
        showConfirmDialogTypeSwitch.value = false;
        activeType.value = nextType.value;
        nextType.value = null;
        changedValues.value = {};
    }
    const discardChangesTypeSwitch = () => {
        showConfirmDialogTypeSwitch.value = false;
        activeType.value = nextType.value;
        nextType.value = null;
        editTable.value = false
        changedValues.value = {};
        populateJsonStr();
    }
    const jsonDiff = (oldJson: any, newJson: any): boolean => {
        return JSON.stringify(oldJson) !== JSON.stringify(newJson);
    };
    const filterOrganizations = (val: any, update: any) => {
        if(val.length > 0){
            update();
            filteredOrganizations.value = organizations.value.filter((org: any) => org.label.toLowerCase().includes(val.toLowerCase()));
        }
        else{
            update();
            filteredOrganizations.value = organizations.value;
        }
    }
    //this is used to search the api categories that is there role-limit
    //we do have multiple api categories so we need to filter them based on the search query
    const filterApiCategoriesToDisplayOptions = (val: any, update: any) => {
        if(val.length > 0){
            update();
            filteredApiCategoryToDisplayOptions.value = apiCategories.value.filter((role: any) => role.label.toLowerCase().includes(val.toLowerCase()));
        }
        else{
            update();
            filteredApiCategoryToDisplayOptions.value = apiCategories.value;
        }
    }
    const changePagination = (val: { label: string; value: any }) => {
    //used to change the pagination of the table
      selectedPerPage.value = val.value;
      pagination.value.rowsPerPage = val.value;
      qTable.value?.setPagination(pagination.value);
    };
    //here we are filtering the modules based on the selected api category
    const filterModulesBasedOnCategory = () => {
        //here we filter based upon selectedapicategory so we only user can able to see one module at a time 
        //if they apply filter 
        if(selectedApiCategory.value?.value){
            filteredRoleLevelModuleRows.value = roleLevelModuleRows.value.filter((row: any) => row.module_name.toLowerCase() === selectedApiCategory.value?.value.toLowerCase());
        }
        else{
            filteredRoleLevelModuleRows.value = roleLevelModuleRows.value;
        }
    }
    const resetStore = () =>{
        store.dispatch("setApiLimitsByOrgId", {});
        store.dispatch("setRoleLimitsByOrgIdByRole", {});
    }
    return {
        t,
        selectedOrganization,
        organizations,
        updateOrganization,
        isOrgLoading,
        activeTab,
        updateActiveTab ,
        tabs,
        editTable,
        searchQuery,
        resultTotal,
        perPageOptions,
        rolesLimitRows,
        rolesColumns,
        apiLimitsRows,
        apiLimitsColumns,
        pagination,
        editTableWithInput,
        store,
        handleInputChange,
        changedValues,
        cancelChanges,
        saveChanges,
        isEdited,
        generateColumns,
        loading,
        isBulkUpdate,
        outlinedFileDownload,
        outlinedFileUpload,
        uploadedRules,
        outlinedInsertDriveFile,
        getImageURL,
        fileListToDisplay,
        downloadTemplate,
        uploadTemplate,
        uploadError,
        uploadingRules,
        closeBulkUpdate,
        focusedInputId,
        onFocus,
        generateUniqueId,
        filteredData,
        roleLimitsColumns,
        triggerExpand,
        expandedRow,
        roleLevelModuleRows,
        roleLevelLoading,
        getRoleLimitsByOrganization,
        selectedApiCategory,
        rolesToBeDisplayed,
        isApiCategoryLoading,
        filteredRoleLevelModuleRows,
        restrictToNumbers,
        preventNonNumericPaste,
        expandedRole,
        showConfirmDialogTabSwitch,
        showConfirmDialogRowSwitch,
        nextTab,
        saveChangesAndTabSwitch,
        discardChangesTabSwitch,
        saveChangesAndRoleSwitch,
        discardChangesRoleSwitch,
        toBeExpandedRow,
        activeType,
        updateActiveType,
        jsonStrToDisplay,
        nextType,
        showConfirmDialogTypeSwitch,
        saveChangesAndTypeSwitch,
        discardChangesTypeSwitch,
        saveJsonChanges,
        cancelJsonChanges,
        isSavingJson,
        filteredOrganizations,
        filterOrganizations,
        filteredApiCategoryToDisplayOptions,
        filterApiCategoriesToDisplayOptions,
        changePagination,
        selectedPerPage,
        qTable,
        organizationToDisplay,
        filterModulesBasedOnCategory
    }

    }
})
</script>

<style lang="scss" >

.quota-page {
    input[type=number]::-webkit-outer-spin-button,
    input[type=number]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
    }
.quota-tabs {
  border: 1px solid $primary;
  width: 200px;
  border-radius: 4px;
  overflow: hidden;
}
.quota-tabs {
  .q-tab--active {
    background-color: $primary;
    color: $white;
  }

  .q-tab__indicator {
    display: none;
  }

  .q-tab {
    height: 30px;
    min-height: 30px;
  }
}
.title-height {
    height: 40px;
    min-height: 40px;
}
.input-width {
    width: 300px;
}
}
.floating-buttons {
  position: sticky;
  bottom: 0;
  top: 0;
  z-index: 100; /* Ensure it stays on top of table content */
  width: 100%;
}
.dark-theme-page {
    .floating-buttons{
        background-color: $dark;
    }
}
.light-theme-page {
    .floating-buttons{
        background-color: $white;
    }
}
.light-theme-page {
    .editable-cell {
        padding: 0px 10px;
        background-color: #f1f1ee;
    }
    .edited-cell {
        padding-left: 8px !important; // light blue color
    }
    .edited-input {
        background-color: #bfc3f4;
        color: black; // blue text color for edited values
        font-weight: 500;
    }
    .edited-input-role-level {
        color: #2196F3; // blue text color for edited values
        font-weight: 500;
        
    }
}

.dark-theme-page {
    .editable-cell {
        padding: 0px 10px;
    }
    .edited-cell {
        padding-left: 8px !important;
    }
    .edited-input {
        background-color: #f6f6f6;
        color: black;
        font-weight: 500;
        padding: 0px !important;
    }
    .edited-input-role-level {
        color: #64B5F6; // lighter blue text color for dark theme
        font-weight: 500;
        width: 100% !important;
    }
}
.file-upload-input {
  height: 100% !important;
}

.file-upload-input > .q-field__inner {
  height: 100% !important;
}

.file-upload-input > .q-field__inner > .q-field__control {
  height: 100% !important;
}

.file-upload-input > .q-field__inner > .q-field__control .q-field__control-container {
  height: 100% !important;
}
.file-upload-input > .q-field__inner > .q-field__control .q-field__control-container .q-field__label {
    height: 100% !important;
    width: 100% !important;
    display: flex;
    align-items: start;
    justify-content: start;

}
.focused-input {
  border: 1px solid #007bff; /* Customize the border color */
  box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); /* Optional: Add shadow for better focus effect */
  width: 100% !important;
}

.expanded-content {
  padding: 0  3rem;
  max-height: 100vh; /* Set a fixed height for the container */
  overflow: hidden; /* Hide overflow by default */
}

.scrollable-content {
  width: 100%; /* Use the full width of the parent */
  overflow-y: auto; /* Enable vertical scrolling for long content */
  padding: 10px; /* Optional: padding for aesthetics */
  border: 1px solid #ddd; /* Optional: border for visibility */
  height: 100%;
  max-height: 200px;
   /* Use the full height of the parent */
  text-wrap: normal;
  background-color: #e8e8e8;
  color: black;
}
.expanded-sql{
  border-left: #7A54A2 3px solid;
}


.app-table-container {
  .thead-sticky,
  .tfoot-sticky {
    position: sticky;
    top: 0;
    opacity: 1;
    z-index: 1;
    background: #f5f5f5;
    
  }

  .q-table--dark .thead-sticky,
  .q-table--dark .tfoot-sticky {
    background: #565656 !important;
  }

  .q-table__bottom {
    .q-table__control {
      padding-top: 0;
      padding-bottom: 0;
    }
  }
}

.editable-input{
    height: 10px !important;
}

.category-select{
    .q-placeholder{
        font-size: 14px !important;
    }
}
.org-select{
    .q-placeholder{
        font-size: 14px !important;
    }
}


</style>